package ac.technion.StreamMania;

import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;


import ac.technion.StreamMania.R;
import android.app.ListActivity;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

/** The main FileBrowser class.  the file browser extends ListActivity and uses
 *  an arraylist of Iconified text to run the file browser.
 */
public class AndroidFileBrowser extends ListActivity 
{
	private enum DISPLAYMODE{ ABSOLUTE, RELATIVE}; 

	private final DISPLAYMODE mDisplayMode = DISPLAYMODE.ABSOLUTE; //this field defines if we see the whole file path or just the name, isn't changed after start of program
	private List<IconifiedText> mDirectoryEntries = new ArrayList<IconifiedText>();
	private File mCurrentDirectory = new File("/");

	private static Set<String> legalExtensions = new HashSet<String>();
	
	/** Called when the activity is first created.
	 * Sets the background, calls a function to fill the set of
	 * legal extentions, and then finds the root of the filesystem. */
	@Override
	public void onCreate(Bundle icicle) 
	{
	    //setTheme(android.R.style.Theme_NoTitleBar_Fullscreen);		
	    setTheme(R.style.Theme_FadeInOut);   
		super.onCreate(icicle);
     
        getListView().setBackgroundResource(R.drawable.backfilebrowser);
        getListView().setCacheColorHint(0);

		// setContentView() gets called within the next line,
		// so we do not need it here. ;
		fillLegalExtensions();
		browseToRoot();
	}

	/**
	 * Adds the legal media extentions to the set.
	 */
	private void fillLegalExtensions() {
		legalExtensions.add("mp4");
		legalExtensions.add("mp3");
		legalExtensions.add("3gp");
		legalExtensions.add("m4a");
		legalExtensions.add("mid");
		legalExtensions.add("ogg");
		legalExtensions.add("wav");
	}
	
	/**
	 * This method browses to the root-directory of the file-system.
	 */ 
	private void browseToRoot()
	{
		browseTo(new File("/"));
	}

	/**
	 * Method browses to the given directory and either lists its files or returns
	 * the file name to the calling activity via an intent if it is not a directory
	 * but a file instead.  It also filters out all hidden/prohibited files and
	 * files which don't have one of the legal extensions.
	 * 
	 * @param dir the file or directory to browse to
	 */
	private void browseTo(final File dir)
	{
		File original = mCurrentDirectory;
		if(dir.isDirectory())
		{
			//file is a directory.  calls functions to set up new directory view.
			mCurrentDirectory = dir;
			try{
				FileFilter filter = new FileFilter() {
					String fileName, extension;
					public boolean accept(File file) {
						boolean fileType=true;
						if(!file.isDirectory()){
							//String type = getMIMEType(file);
							fileName = file.toString();
							int mid= fileName.lastIndexOf(".");
							extension=fileName.substring(mid+1,fileName.length());
							//fileType = type.equalsIgnoreCase("audio/*");
							fileType = legalExtensions.contains(extension);
						}
						return file.canRead() && !file.isHidden() && fileType;
					}
				}; 
				File[] f =mCurrentDirectory.listFiles(filter);
				if(f==null)
					throw new Exception("File is Null for some odd reason");
				fill(f);//adds files for this directory to mDirectoryEntries
			}
			catch(Exception  S){
				//we get here apparently when the file is hidden or can't be accessed for security reasons
				Context context = getApplicationContext();
				CharSequence text = "Can't open this file. "+S.getMessage();
				int duration = Toast.LENGTH_SHORT;
				Toast toast = Toast.makeText(context, text, duration);
				toast.setGravity(Gravity.CENTER, 0, 0);
				toast.show();  
				mCurrentDirectory = original;
			}
		}
		else
		{
			Intent data = new Intent();
			data.putExtra("ac.technion.StreamMania.fileName", dir.getAbsolutePath());
			//PendingIntent pIntent = createPendingResult(0, data, PendingIntent.FLAG_CANCEL_CURRENT);
			setResult(RESULT_OK, data);
			finish();
	
		}
	}

	/**
	 * Receives an array of files belonging to the current directory
	 * Fills the list view with the file path (relative or absolute).
	 * Also identifies and uses the correct icon for each file/folder.
	 * 
	 * @param files the files to be shown
	 */
	private void fill(File[] files) 
	{
		this.mDirectoryEntries.clear();//clears list of files for current directory

        // Add the "." == "current directory"
        this.mDirectoryEntries.add(new IconifiedText(getString(R.string.current_level),getResources().getDrawable(R.drawable.refresh)));      
        
        // and the ".." == 'Up one level'
        if(this.mCurrentDirectory.getParent() != null)
             this.mDirectoryEntries.add(new IconifiedText(getString(R.string.up_one_level),getResources().getDrawable(R.drawable.uponelevel)));
        Drawable currentIcon = null;
		for (File currentFile : files){
			if (currentFile.isDirectory()) {
				currentIcon = getResources().getDrawable(R.drawable.folder);
			}else{
				String fileName = currentFile.getName();
				/* Determine the Icon to be used, 
				 * depending on the FileEndings defined in:
				 * res/values/fileendings.xml. */
				if(checkEndsWithInStringArray(fileName, getResources().
								getStringArray(R.array.fileEndingAudio))){
					currentIcon = getResources().getDrawable(R.drawable.audio);
				}else if(checkEndsWithInStringArray(fileName, getResources().
								getStringArray(R.array.fileEndingVideo))){
					currentIcon = getResources().getDrawable(R.drawable.video);
				}else{
					currentIcon = getResources().getDrawable(R.drawable.icon);
				}				
			}
			switch(this.mDisplayMode)
			{
			case ABSOLUTE: //adds the file's name with it's entire path to the string array
				this.mDirectoryEntries.add(new IconifiedText(currentFile.getPath(), currentIcon));
                break;
			case RELATIVE: // On relative Mode, we have to substract the current-path from the beginning
				int currentPathStringLenght = this.mCurrentDirectory.getAbsolutePath().length();
				this.mDirectoryEntries.add(new IconifiedText(currentFile.getAbsolutePath().substring(currentPathStringLenght),currentIcon));
                  break;
			}
		}

		Collections.sort(this.mDirectoryEntries);
	     //   this.getCurrentFocus();
	        // create an array list adapter and set it as the view
		IconifiedTextListAdapter itla = new IconifiedTextListAdapter(this);
		itla.setListItems(this.mDirectoryEntries);		
		this.setListAdapter(itla);
	}


	/**
	 * This function browses up one level according to the field: mCurrentDirectory.
	 */
	private void upOneLevel()
	{
		if(this.mCurrentDirectory.getParent() != null)
			this.browseTo(this.mCurrentDirectory.getParentFile());
	} 

	/**
	 * Overrides OnListItemClick.  When a listItem is chosen the function calls
	 * browseto or uponelevel to browse to the correct directory, or to choose
	 * the file to play.
	 */
	@Override
	protected void onListItemClick(ListView l, View v, int position, long id) {
		super.onListItemClick(l, v, position, id);
    	int selectionRowID = position;
    	String selectedFileString = this.mDirectoryEntries.get(position).getText();
    	if (selectedFileString.equals(getString(R.string.current_level))) 
		{
			// Refresh
			this.browseTo(this.mCurrentDirectory);
		} else if(selectedFileString.equals(getString(R.string.up_one_level)))
		{
			this.upOneLevel();
		} 
		else 
		{
			File clickedFile = null;
			switch(this.mDisplayMode){
			case RELATIVE:
				clickedFile = new File(this.mCurrentDirectory.getAbsolutePath()
						+ this.mDirectoryEntries.get(selectionRowID));
				break;
			case ABSOLUTE:
				clickedFile = new File(selectedFileString);
				break;
			}
			if(clickedFile != null)
			{
				Log.d("AndroidFileBrowser", "File " + clickedFile + " exists? " +
						clickedFile.exists());
				this.browseTo(clickedFile);
			}
		} 
	}
	
	   /** Checks whether checkItsEnd ends with
     * one of the Strings from fileEndings */
    private boolean checkEndsWithInStringArray(String checkItsEnd,
                        String[] fileEndings){
         for(String aEnd : fileEndings){
              if(checkItsEnd.endsWith(aEnd))
                   return true;
         }
         return false;
    }
}
